using System.Net.Sockets;
using System.Threading;

namespace System.Net.DnsToolkit.SocketAsync
{
    /// <summary>
    /// Based on example from http://msdn2.microsoft.com/en-us/library/bb517542.aspx
    /// This class creates a single large buffer which can be divided up 
    /// and assigned to SocketAsyncEventArgs objects for use with each 
    /// socket I/O operation.  
    /// This enables bufffers to be easily reused and guards against 
    /// fragmenting heap memory.
    /// </summary>
    /// <remarks>The operations exposed on the BufferManager class are not thread safe.</remarks>
    internal sealed class BufferManager
    {
        /// <summary>
        /// The underlying Byte array maintained by the Buffer Manager.
        /// </summary>
        private Byte[] buffer;                

        /// <summary>
        /// Size of the underlying Byte array.
        /// </summary>
        private Int32 bufferSize;

        /// <summary>
        /// Current index of the underlying Byte array.
        /// </summary>
        private Int32 currentIndex;

        /// <summary>
        /// The total number of bytes controlled by the buffer pool.
        /// </summary>
        private Int32 numBytes;

        /// <summary>
        /// Instantiates a buffer manager.
        /// </summary>
        /// <param name="totalBytes">The total number of bytes for the buffer pool.</param>
        /// <param name="bufferSize">Size of the buffer pool.</param>
        internal BufferManager(Int32 totalBytes, Int32 bufferSize)
        {
            numBytes = totalBytes;
            currentIndex = 0;
            this.bufferSize = bufferSize;
            // Create one big large buffer and divide that out to each SocketAsyncEventArg object.
            buffer = new Byte[numBytes];
        }

        /// <summary>
        /// Removes the buffer from a SocketAsyncEventArg object. 
        /// This frees the buffer back to the buffer pool.
        /// </summary>
        /// <param name="args">SocketAsyncEventArgs where is the buffer to be removed.</param>
        internal void ResetBuffer(SocketAsyncEventArgs saea)
        {
            if (saea == null) {
                throw new ArgumentNullException("saea");
            }
            // BUG 015
            saea.SetBuffer(saea.Offset, bufferSize);
            Array.Clear(saea.Buffer, saea.Offset, bufferSize);
        }

        /// <summary>
        /// Assigns a buffer from the buffer pool to the specified SocketAsyncEventArgs object.
        /// </summary>
        /// <param name="args">SocketAsyncEventArgs where is the buffer to be allocated.</param>
        /// <returns>True if the buffer was successfully set, else false.</returns>
        internal void SetBuffer(SocketAsyncEventArgs saea) {
            // BUG 10
            if ((numBytes - bufferSize) < currentIndex) {
                throw new ArgumentOutOfRangeException("buffer");
            }
            saea.SetBuffer(buffer, currentIndex, bufferSize);
            Interlocked.Add(ref currentIndex, bufferSize);

        }
    }
}
